﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Diagnostics;
using System.Linq;
using System.Text;
using DevWeb.Biztalk.DeploymentSettings.Data.CodeFirst.Configurations;
using TrackerEnabledDbContext;
using System.Threading.Tasks;

using TrackerEnabledDbContext.Common.Models;

namespace DevWeb.Biztalk.DeploymentSettings.Data.CodeFirst.Context
{
    public class DeploymentSettingsContext : TrackerContext, IDatabaseContext
    {

       
        public DbSet<Integration> Integrations { get; set; }
        public DbSet<IntegrationVersion> IntegrationVersions { get; set; }
        public DbSet<TargetEnvironment> TargetEnvironments { get; set; }

        public DbSet<IntegrationVersionParameter> IntegrationVersionParameters { get; set; }
        public DbSet<IntegrationVersionParameterValue> IntegrationVersionParameterValues { get; set; }
        public DbSet<DefaultSettingParameter> DefaultSettingParameters { get; set; }
        public DbSet<DefaultSettingParameterValue> DefaultSettingParameterValues { get; set; }
        public DbSet<DeploymentLog> DeploymentLogs { get; set; }

        public DbSet<UserEnvironment> UserEnvironments { get; set; }

        public DbSet<UserEnvironmentRight> UserEnvironmentRights { get; set; }

        public DeploymentSettingsContext()
            : base("DeploymentSettingsConnection")
        {

            
            Init();
        }

        private void Init()
        {
            Configuration.ProxyCreationEnabled = false;
            Configuration.LazyLoadingEnabled = true;
            
            
           
            if (!base.Database.Exists())
            {
                base.Database.CreateIfNotExists();

                SetupSeed();
            }
           
        }

        private void SetupSeed()
        {
            CheckBasicEnvironments("DEV");
            CheckBasicEnvironments("TST");
            CheckBasicEnvironments("ACC");
            CheckBasicEnvironments("PRD");
            SaveChanges();
        }

        private void CheckBasicEnvironments(string environment)
        {

            try
            {
                var devenv = TargetEnvironments.SingleOrDefault(p => p.Name.Equals(environment.ToUpper()));

                if (devenv == null)
                {
                    var newenvironment = new TargetEnvironment()
                    {
                        Name = environment,
                        Description = "Initial Created " + environment
                    };

                    TargetEnvironments.Add(newenvironment);
                }
            }
            catch (Exception exception)
            {
                
                Trace.Write(exception);
            }
           
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {

            base.Configuration.AutoDetectChangesEnabled = true;
            modelBuilder.Configurations.Add(new IntegrationConfiguration());
            modelBuilder.Configurations.Add(new IntegrationVersionConfiguration());
            modelBuilder.Configurations.Add(new TargetEnvironmentConfiguration());
            modelBuilder.Configurations.Add(new DefaultSettingParameterConfiguration());
            modelBuilder.Configurations.Add(new IntegrationVersionParameterConfiguration());
            modelBuilder.Configurations.Add(new IntegrationVersionParameterValueConfiguration());
            modelBuilder.Configurations.Add(new DeploymentLogConfiguration());
           
           
            
            base.OnModelCreating(modelBuilder);
        }

        protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
        {
            string type = entityEntry.Entity.GetType().FullName;

            Trace.WriteLine(entityEntry);
            return base.ValidateEntity(entityEntry, items);
        }

        public override int SaveChanges()
        {


            return base.SaveChanges(UserName);
        }



        public string UserName
        {
            get;
            set;
        }


     


        public Database GetDatabase()
        {
            return this.Database;
        }


        public int SaveChanges(string userName)
        {

            var now = DateTime.UtcNow;

            var modifiedEntities = ChangeTracker.Entries()
        .Where(p => p.State == EntityState.Modified).ToList();

            var deletedEntities = ChangeTracker.Entries()
        .Where(p => p.State == EntityState.Deleted).ToList();

            var AddedEntities = ChangeTracker.Entries()
        .Where(p => p.State == EntityState.Added).ToList();

            try
            {
                List<AuditLog> logitems = new List<AuditLog>();
                ProcessChange(modifiedEntities, userName, EventType.Modified, ref logitems);
                ProcessChange(deletedEntities, userName, EventType.Deleted, ref logitems);

                var returnvalue = base.SaveChanges(userName);

                ///first save added items cause we need a primary key... 
                ProcessChange(AddedEntities, userName, EventType.Added, ref logitems);

                Task logtask = Task.Factory.StartNew(() => SaveAuditLogs(logitems));

       




                logtask.Wait();
                return returnvalue;
            }
            catch (Exception ex)
            {

                throw ex;
            }

        }

        private void ProcessChange(List<DbEntityEntry> Entities, object username, EventType eventype, ref List<AuditLog> logitems)
        {


            foreach (DbEntityEntry change in Entities)
            {

                Type t = change.Entity.GetType();
                var entityName = t.Name;

                // var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(change.Entity);

                //  objectStateEntry.GetUpdatableOriginalValues();

                var originalvalues = ((eventype != EventType.Added) || (eventype != EventType.Deleted)) ? change.GetDatabaseValues() : null;
                

                var primaryKey = GetPrimaryKeyValue(change);

                AuditLog log = new AuditLog()
                {
                    UserName = username.ToString(),
                    EventDateUTC = DateTime.UtcNow,
                    EventType = eventype,
                    TypeFullName = entityName,
                    RecordId = primaryKey.ToString()
                };

                if (eventype != EventType.Deleted)
                {
                    var currentvalues = change.CurrentValues;
                    foreach (var propchange in change.CurrentValues.PropertyNames)
                    {


                        var originalValue = GetValueOrNull(originalvalues, propchange);
                        var currentValue = GetValueOrNull(currentvalues, propchange);
                        if (originalValue != currentValue)
                        {
                            AuditLogDetail detail = new AuditLogDetail();
                            detail.OriginalValue = originalValue;
                            detail.PropertyName = propchange.ToString();
                            detail.NewValue = currentValue;


                            log.LogDetails.Add(detail);

                        }


                    }
                }

                logitems.Add(log);
            }

        }


        private string GetValueOrNull(DbPropertyValues values, string propname)
        {

            try
            {
                if (values == null)
                {
                    return string.Empty;
                }
                if (values[propname] == null)
                {
                    return string.Empty;
                }
                else
                {
                    return values[propname].ToString();
                }
            }
            catch (Exception)
            {

                return string.Empty;
            }

        }

        object GetPrimaryKeyValue(DbEntityEntry entry)
        {
            var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
            return objectStateEntry.EntityKey.EntityKeyValues[0].Value;
        }

        public void SaveAuditLogs(List<AuditLog> logitems)
        {



            try
            {
                foreach (var item in logitems)
                {
                    AuditLog.Add(item);
                    LogDetails.AddRange(item.LogDetails);
                }
                //SaveChanges();


            }
            catch (Exception)
            {


            }


        }

    }

   
}
